﻿using System.Linq.Expressions;

using Tszy.Unlimited.Base.Exceptions;
using Tszy.Unlimited.Data.AdoNet.Sql.Components.Expressions.Values;
using Tszy.Unlimited.Data.AdoNet.Sql.Descriptors;

namespace Tszy.Unlimited.Data.AdoNet.Sql.Components.Expressions
{
    internal abstract class MathExpressionExecutorBase : ExpressionExecutorBase
    {
        /// <summary>
        ///
        /// </summary>
        /// <param name="name"></param>
        protected MathExpressionExecutorBase(string name) : base(name)
        {
        }

        protected virtual IWhereMathExpressionDescriptor GetWhereMathExpressionDescriptor(ExpressionContext context, Expression expression)
        {
            if (expression == null)
                throw new MainApplicationException($"{Name} 函数处理失败，表达式不可以是 null。");

            if (expression.NodeType == ExpressionType.Lambda)
            {
                var lambdaExpression = (LambdaExpression)expression;

                switch (lambdaExpression.Body.NodeType)
                {
                    case ExpressionType.Add:
                    // a + b

                    case ExpressionType.Subtract:
                    // a - b

                    case ExpressionType.Increment:
                    // a + 1l

                    case ExpressionType.Decrement:
                    // a - 1

                    case ExpressionType.UnaryPlus:
                    // +a

                    case ExpressionType.Negate:
                    // -a

                    case ExpressionType.Multiply:
                    // a * b

                    case ExpressionType.MultiplyAssign:
                    // a *= b

                    case ExpressionType.Divide:
                    // a / b

                    case ExpressionType.Modulo:
                        // a % b
                        return MathExpressionBuild(context, lambdaExpression);

                    default:
                        return GetDefaultAverageValue(context, expression);
                }
            }
            else
            {
                throw new MainApplicationException($"{Name} 函数处理失败，表达式不正确。");
            }
        }

        private IWhereMathExpressionDescriptor GetDefaultAverageValue(ExpressionContext context, Expression expression)
        {
            return new WhereMathExpressionDescriptor
            {
                Value = new WhereMathExpressionParameter
                {
                    ParmeterName = GetParameterNameByLambda(context, expression)
                }
            };
        }

        private IWhereMathExpressionDescriptor MathExpressionBuild(ExpressionContext context, LambdaExpression lambdaExpression)
        {
            Expression expression = null;

            if (lambdaExpression.Body.NodeType == ExpressionType.Negate)
            {
                expression = lambdaExpression.Body as UnaryExpression;
            }
            else
            {
                expression = lambdaExpression.Body as BinaryExpression;
            }

            return MathExpressionWhereBuild(context, expression);
        }

        private IWhereMathExpressionDescriptor MathExpressionWhereBuild(ExpressionContext context, Expression expression)
        {
            if (expression is BinaryExpression)
            {
                var binaryExpression = expression as BinaryExpression;

                switch (binaryExpression.NodeType)
                {
                    case ExpressionType.Add:
                        // a + b
                        return MathExpressionAddBuild(context, binaryExpression);

                    case ExpressionType.Subtract:
                        // a - b
                        return MathExpressionSubtractBuild(context, binaryExpression);

                    case ExpressionType.Increment:
                        // a + 1
                        return MathExpressionAddBuild(context, binaryExpression);

                    case ExpressionType.Decrement:
                        // a - 1
                        return MathExpressionSubtractBuild(context, binaryExpression);

                    case ExpressionType.Multiply:
                        // a * b
                        return MathExpressionMultiplyBuild(context, binaryExpression);

                    case ExpressionType.MultiplyAssign:
                        // a *= b
                        return MathExpressionMultiplyAssignBuild(context, binaryExpression);

                    case ExpressionType.Divide:
                        // a / b
                        return MathExpressionDivideBuild(context, binaryExpression);

                    case ExpressionType.Modulo:
                        // a % b
                        return MathExpressionModuloBuild(context, binaryExpression);

                    default:
                        throw new MainApplicationException($"{Name} 函数处理失败，暂时不支持该表达式。");
                }
            }
            else
            {
                var unaryExpression = expression as UnaryExpression;

                switch (unaryExpression.NodeType)
                {
                    case ExpressionType.UnaryPlus:
                        // +a
                        return MathExpressionUnaryPlusBuild(context, unaryExpression);

                    case ExpressionType.Negate:
                        // -a
                        return MathExpressionNegateBuild(context, unaryExpression);

                    default:
                        throw new MainApplicationException($"{Name} 函数处理失败，表达式不正确。");
                }
            }
        }

        private static bool IsExpression(Expression expression)
        {
            switch (expression.NodeType)
            {
                case ExpressionType.Add:
                    // a + b
                    return true;

                case ExpressionType.Subtract:
                    // a - b
                    return true;

                case ExpressionType.Increment:
                    // a + 1
                    return true;

                case ExpressionType.Decrement:
                    // a - 1
                    return true;

                case ExpressionType.UnaryPlus:
                    // +a
                    return true;

                case ExpressionType.Negate:
                    // -a
                    return true;

                case ExpressionType.Multiply:
                    // a * b
                    return true;

                case ExpressionType.MultiplyAssign:
                    // a *= b
                    return true;

                case ExpressionType.Divide:
                    // a / b
                    return true;

                case ExpressionType.Modulo:
                    // a % b
                    return true;

                default:
                    return false;
            }
        }

        private string GetValue(ExpressionContext context, Expression expression)
        {
            var result = ValueExpressionHandler.Handler(context, expression);

            if (result.Type == ValueExpressionType.Parameter)
            {
                var parameterName = $"{result?.Value}";

                return parameterName;
            }

            return string.Empty;
        }

        /// <summary>
        /// a + b
        /// </summary>
        /// <param name="context"></param>
        /// <param name="expression"></param>
        /// <returns></returns>
        private IWhereMathExpressionDescriptor MathExpressionAddBuild(ExpressionContext context, BinaryExpression expression)
        {
            IWhereMathExpressionDescriptor left = null;
            IWhereMathExpressionDescriptor right = null;

            if (IsExpression(expression.Left))
            {
                left = MathExpressionWhereBuild(context, expression.Left);
            }
            else
            {
                left = new WhereMathExpressionDescriptor
                {
                    Value = new WhereMathExpressionParameter
                    {
                        ParmeterName = GetValue(context, expression.Left)
                    }
                };
            }

            if (IsExpression(expression.Right))
            {
                right = MathExpressionWhereBuild(context, expression.Right);
            }
            else
            {
                right = new WhereMathExpressionDescriptor
                {
                    Value = new WhereMathExpressionParameter
                    {
                        ParmeterName = GetValue(context, expression.Right)
                    }
                };
            }

            return new WhereMathExpressionDescriptor
            {
                ExpressionDescriptor = new WhereMathDetailExpressionDescriptor
                {
                    Left = left,
                    Right = right,
                    Tag = "+"
                }
            };
        }

        private IWhereMathExpressionDescriptor MathExpressionUnaryPlusBuild(ExpressionContext context, UnaryExpression expression)
        {
            return new WhereMathExpressionDescriptor
            {
                Value = new WhereMathExpressionParameter
                {
                    ParmeterName = GetValue(context, expression.Operand)
                }
            };
        }

        /// <summary>
        /// a - b
        /// </summary>
        /// <param name="context"></param>
        /// <param name="expression"></param>
        /// <returns></returns>
        private IWhereMathExpressionDescriptor MathExpressionSubtractBuild(ExpressionContext context, BinaryExpression expression)
        {
            IWhereMathExpressionDescriptor left = null;
            IWhereMathExpressionDescriptor right = null;

            if (IsExpression(expression.Left))
            {
                left = MathExpressionWhereBuild(context, expression.Left);
            }
            else
            {
                left = new WhereMathExpressionDescriptor
                {
                    Value = new WhereMathExpressionParameter
                    {
                        ParmeterName = GetValue(context, expression.Left)
                    }
                };
            }

            if (IsExpression(expression.Right))
            {
                right = MathExpressionWhereBuild(context, expression.Right);
            }
            else
            {
                right = new WhereMathExpressionDescriptor
                {
                    Value = new WhereMathExpressionParameter
                    {
                        ParmeterName = GetValue(context, expression.Right)
                    }
                };
            }

            return new WhereMathExpressionDescriptor
            {
                ExpressionDescriptor = new WhereMathDetailExpressionDescriptor
                {
                    Left = left,
                    Right = right,
                    Tag = "-"
                }
            };
        }

        /// <summary>
        /// -a
        /// </summary>
        /// <param name="context"></param>
        /// <param name="expression"></param>
        /// <returns></returns>
        private IWhereMathExpressionDescriptor MathExpressionNegateBuild(ExpressionContext context, UnaryExpression expression)
        {
            return new WhereMathExpressionDescriptor
            {
                Value = new WhereMathExpressionParameter
                {
                    ParmeterName = GetValue(context, expression.Operand),
                    Tag = "-"
                }
            };
        }

        /// <summary>
        /// a * b
        /// </summary>
        /// <param name="context"></param>
        /// <param name="expression"></param>
        /// <returns></returns>
        private IWhereMathExpressionDescriptor MathExpressionMultiplyBuild(ExpressionContext context, BinaryExpression expression)
        {
            IWhereMathExpressionDescriptor left = null;
            IWhereMathExpressionDescriptor right = null;

            if (IsExpression(expression.Left))
            {
                left = MathExpressionWhereBuild(context, expression.Left);
            }
            else
            {
                left = new WhereMathExpressionDescriptor
                {
                    Value = new WhereMathExpressionParameter
                    {
                        ParmeterName = GetValue(context, expression.Left)
                    }
                };
            }

            if (IsExpression(expression.Right))
            {
                right = MathExpressionWhereBuild(context, expression.Right);
            }
            else
            {
                right = new WhereMathExpressionDescriptor
                {
                    Value = new WhereMathExpressionParameter
                    {
                        ParmeterName = GetValue(context, expression.Right)
                    }
                };
            }

            return new WhereMathExpressionDescriptor
            {
                ExpressionDescriptor = new WhereMathDetailExpressionDescriptor
                {
                    Left = left,
                    Right = right,
                    Tag = "*"
                }
            };
        }

        /// <summary>
        /// a *= b
        /// </summary>
        /// <param name="context"></param>
        /// <param name="expression"></param>
        /// <returns></returns>
        private IWhereMathExpressionDescriptor MathExpressionMultiplyAssignBuild(ExpressionContext context, BinaryExpression expression)
        {
            throw new MainApplicationException($"{Name} 函数处理失败，暂时不支持该表达式。");

            //var left = string.Empty;
            //var right = string.Empty;

            //if (IsExpression(expression.Left))
            //{
            //    left = MathExpressionWhereBuild(context, expression.Left);
            //}
            //else
            //{
            //    left = GetValue(context, expression.Left);
            //}

            //if (IsExpression(expression.Right))
            //{
            //    right = MathExpressionWhereBuild(context, expression.Right);
            //}
            //else
            //{
            //    right = GetValue(context, expression.Right);
            //}

            //return string.Empty;
        }

        /// <summary>
        /// a / b
        /// </summary>
        /// <param name="context"></param>
        /// <param name="expression"></param>
        /// <returns></returns>
        private IWhereMathExpressionDescriptor MathExpressionDivideBuild(ExpressionContext context, BinaryExpression expression)
        {
            IWhereMathExpressionDescriptor left = null;
            IWhereMathExpressionDescriptor right = null;

            if (IsExpression(expression.Left))
            {
                left = MathExpressionWhereBuild(context, expression.Left);
            }
            else
            {
                left = new WhereMathExpressionDescriptor
                {
                    Value = new WhereMathExpressionParameter
                    {
                        ParmeterName = GetValue(context, expression.Left)
                    }
                };
            }

            if (IsExpression(expression.Right))
            {
                right = MathExpressionWhereBuild(context, expression.Right);
            }
            else
            {
                right = new WhereMathExpressionDescriptor
                {
                    Value = new WhereMathExpressionParameter
                    {
                        ParmeterName = GetValue(context, expression.Right)
                    }
                };
            }

            return new WhereMathExpressionDescriptor
            {
                ExpressionDescriptor = new WhereMathDetailExpressionDescriptor
                {
                    Left = left,
                    Right = right,
                    Tag = "/"
                }
            };
        }

        /// <summary>
        /// a % b
        /// </summary>
        /// <param name="context"></param>
        /// <param name="expression"></param>
        /// <returns></returns>
        private IWhereMathExpressionDescriptor MathExpressionModuloBuild(ExpressionContext context, BinaryExpression expression)
        {
            IWhereMathExpressionDescriptor left = null;
            IWhereMathExpressionDescriptor right = null;

            if (IsExpression(expression.Left))
            {
                left = MathExpressionWhereBuild(context, expression.Left);
            }
            else
            {
                left = new WhereMathExpressionDescriptor
                {
                    Value = new WhereMathExpressionParameter
                    {
                        ParmeterName = GetValue(context, expression.Left)
                    }
                };
            }

            if (IsExpression(expression.Right))
            {
                right = MathExpressionWhereBuild(context, expression.Right);
            }
            else
            {
                right = new WhereMathExpressionDescriptor
                {
                    Value = new WhereMathExpressionParameter
                    {
                        ParmeterName = GetValue(context, expression.Right)
                    }
                };
            }

            return new WhereMathExpressionDescriptor
            {
                ExpressionDescriptor = new WhereMathDetailExpressionDescriptor
                {
                    Left = left,
                    Right = right,
                    Tag = "%"
                }
            };
        }
    }
}